

#import "network.h"
#import "NSURLSession+cagegory.h"

@interface network ()<NSURLSessionDelegate>
@property (nonatomic, strong) successBlock success;
@property (nonatomic, strong) failBlock fail;
@property (nonatomic, strong) progressBlock loadProgress;
@property (nonatomic, strong) NSMutableData *responesData;
@property (nonatomic, strong) NSProgress *progress;
@property (nonatomic, copy) NSString *action;//判断是download还是upload
@end
@implementation network
static network *_network;
static dispatch_once_t onceToken;

+ (instancetype)http
{
    if (!_network)
    {
        dispatch_once(&onceToken, ^{
            _network = [[network alloc] init];
            _network.basicURLString = baseuserl;
            _network.type = CCRequestContentTypeJson;
            _network.basicPara = [NSMutableDictionary dictionary];
        });
    }
    return _network;
}

- (NSProgress *)progress
{
    if (!_progress)
    {
        _progress = [[NSProgress alloc] init];
    }
    return _progress;
}

- (NSMutableData *)responesData
{
    if (!_responesData)
    {
        _responesData = [[NSMutableData alloc] init];
    }
    return _responesData;
}

+ (NSURLSessionTask *)get:(nullable NSString *)api para:(nullable NSDictionary *)para header:(nullable NSDictionary *)header success:(successBlock)success fail:(failBlock)fail
{
    NSURLSessionTask *task = [self dataTaskWithHTTPMethod:@"GET" api:api para:header header:header  data:nil  action:nil uploadProgress:nil downloadProgress:nil success:success fail:fail];
    [task resume];
    return task;
}

+ (NSURLSessionTask *)post:(nullable NSString *)api para:(nullable NSDictionary *)para success:(successBlock)success fail:(failBlock)fail
{
    NSURLSessionTask *task = [self dataTaskWithHTTPMethod:@"POST" api:api para:para header:nil data:nil action:nil uploadProgress:nil downloadProgress:nil success:success fail:fail];
    [task resume];
    return task;
}
+ (NSURLSessionTask *)post:(nullable NSString *)api para:(nullable NSDictionary *)para header:(nullable NSDictionary *)header success:(successBlock)success fail:(failBlock)fail
{
    NSURLSessionTask *task = [self dataTaskWithHTTPMethod:@"POST" api:api para:para header:header data:nil action:nil uploadProgress:nil downloadProgress:nil success:success fail:fail];
    [task resume];
    return task;
}
+ (NSURLSessionTask *)uploadBase64:(nullable NSString *)api para:(nullable NSDictionary *)para data:(nonnull NSData *)data progress:(progressBlock)progress success:(successBlock)success fail:(failBlock)fail
{
    NSURLSessionTask *task = [self dataTaskWithHTTPMethod:@"POST" api:api para:para header:nil data:data == nil ? nil : @[data] action:@"uploadBase64" uploadProgress:progress downloadProgress:nil success:success fail:fail];
    [task resume];
    return task;
}
+ (NSURLSessionTask *)upload:(nullable NSString *)api para:(nullable NSDictionary *)para data:(nonnull NSData *)data progress:(progressBlock)progress success:(successBlock)success fail:(failBlock)fail
{
    NSURLSessionTask *task = [self dataTaskWithHTTPMethod:@"POST" api:api para:para header:nil data:@[data] action:@"upload" uploadProgress:progress downloadProgress:nil success:success fail:fail];
    [task resume];
    return task;
}
+ (NSURLSessionTask *)upload:(nullable NSString *)api para:(nullable NSDictionary *)para dataArray:(nonnull NSArray <NSData *> *)dataArray progress:(progressBlock)progress success:(successBlock)success fail:(failBlock)fail
{
    NSURLSessionTask *task = [self dataTaskWithHTTPMethod:@"POST" api:api para:para header:nil data:dataArray action:@"upload" uploadProgress:progress downloadProgress:nil success:success fail:fail];
    [task resume];
    return task;
}
+ (NSURLSessionDownloadTask *)download:(nullable NSString *)api para:(nullable NSDictionary *)para progress:(progressBlock)progress success:(successBlock)success fail:(failBlock)fail
{
    NSURLSessionDownloadTask *task = (NSURLSessionDownloadTask*)[self dataTaskWithHTTPMethod:@"GET" api:api para:para header:nil data:nil action:@"download" uploadProgress:nil downloadProgress:progress success:success fail:fail];
    [task resume];
    return task;
}

+ (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)data session:(NSURLSession *)session
{
    NSURLSessionDownloadTask *task = [session downloadTaskWithResumeData:data];
    [task resume];
    return task;
}

+ (NSURLSessionTask *)dataTaskWithHTTPMethod:(nonnull NSString *)method
                                         api:(NSString *)api
                                        para:(nullable NSDictionary *)para
                                      header:(nullable NSDictionary *)header
                                        data:(nullable NSArray < NSData *> *)datas
                                      action:(nullable NSString *)action
                              uploadProgress:(nullable progressBlock)uploadProgress
                            downloadProgress:(nullable progressBlock)downloadProgress
                                     success:(nullable successBlock)success
                                        fail:(failBlock)fail
{
    NSURL *URL;
    if ([api containsString:@"http"]) {
        URL = [NSURL URLWithString:api];
    }else{
        URL = [NSURL URLWithString:[[network http].basicURLString stringByAppendingString:api]];
    }
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
    request.HTTPMethod = method;
    request.timeoutInterval = 60;
    NSMutableDictionary *paranew = [NSMutableDictionary dictionaryWithDictionary:[[network http].basicPara mutableCopy]];
    [paranew setValuesForKeysWithDictionary:para];
    
    if ([method hasPrefix:@"POST"])
    {//IOS 13后，GET不能添加request.httpBody
        switch (_network.type) {
            case CCRequestContentTypeJson:{
                [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
                NSError *error;
                //根据Content-Type httpbody的编码方式也不同，这里暂时指定为json，后面遇到不同Content-Typ，再添加对应编码方式
                request.HTTPBody = [NSJSONSerialization dataWithJSONObject:paranew options:NSJSONWritingPrettyPrinted error:&error];
            }
                break;
            case CCRequestContentTypePlist:{
                [request setValue:@"application/x-plist" forHTTPHeaderField:@"Content-Type"];
            }
                break;
            case CCRequestContentTypeXML:{
                [request setValue:@"application/xml" forHTTPHeaderField:@"Content-Type"];
            }
                break;
            case CCRequestContentTypeForm:{
                [request setValue:@"application/x-www-form-urlencoded ;charset=utf-8" forHTTPHeaderField:@"Content-Type"];
                NSMutableString *str = [NSMutableString string];
                for (NSString *key in [paranew allKeys]) {
                    NSString *value = [NSString stringWithFormat:@"%@",[paranew valueForKey:key]];
                    [str appendFormat:@"%@=%@&",key,value];
                }
                NSString *postStr = [str substringWithRange:NSMakeRange(0, str.length - 1)];
                request.HTTPBody = [postStr dataUsingEncoding:NSUTF8StringEncoding];
            }
                break;
            default:
                break;
        }
    }
    if (header)
    {
        //        [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
        [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
        [request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
        for (NSString *key in [header allKeys]) {
            [request setValue:[header valueForKey:key] forKey:key];
        }
    }
    if (_network.header) {
        for (NSString *key in [_network.header allKeys]) {
            [request setValue:[_network.header valueForKey:key] forHTTPHeaderField:key];
        }
    }
    
    network *httpObj = [[network alloc] init];
    httpObj.loadProgress = uploadProgress?uploadProgress:downloadProgress?downloadProgress:nil;
    httpObj.success = success;
    httpObj.fail = fail;
    httpObj.action = action;
    
    NSURLSession *session = [NSURLSession sessionWithDelegate:httpObj];
    if ([action hasPrefix:@"upload"])
        return [session uploadTaskWithRequest:request para:para data:datas];
    else if ([action hasPrefix:@"uploadBase64"])
        return [session uploadBase64WithRequest:request para:para data:datas];
    else if ([action hasPrefix:@"download"])
        return [session downloadTaskWithRequest:request];
    else
        return [session dataTaskWithRequest:request];
}
+ (BOOL)shouldReturnWithResponse:(nullable id)response
{
    return NO;
}

#pragma mark -- NSURLSessionDelegate method
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler
{
    NSLog(@"1。 %s",__FUNCTION__);
    completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
}
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
    NSLog(@"2。 %@ %@  %s",self , dataTask.currentRequest.URL.absoluteString ,__FUNCTION__);
    completionHandler(NSURLSessionResponseAllow);
}
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
    [self.responesData appendData:data];
}
-(void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error
{
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error
{
    if (error)
    {
        if (self.fail)
        {
            dispatch_async(dispatch_get_main_queue(), ^{
                self.fail(error);
            });
        }
        return;
    }
    NSError *dicError = nil;
    NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:self.responesData options:NSJSONReadingMutableLeaves error:&dicError];
    
    ///特别加上自证协议的https情况下，拿不到图片的解决办法
    if (dict == nil) {
        UIImage *img = [UIImage imageWithData:self.responesData];
        dispatch_async(dispatch_get_main_queue(), ^{
            if (self.success) {
                self.success(img);
            }
        });
        return;
    }
    if (self.fail && dicError && self.action == nil)
    {
        dispatch_async(dispatch_get_main_queue(), ^{
            self.fail(dicError);
        });
    }
    else if (self.success)
    {
        dispatch_async(dispatch_get_main_queue(), ^{
            
            if (dict)
            {
                if ([network shouldReturnWithResponse:dict])
                    return [session finishTasksAndInvalidate];
                if ([[dict valueForKey:@"code"] intValue] == 0) {
                    return self.success(dict);
                }else{
                    if ([[dict valueForKey:@"code"] intValue] == 401) {
                        
                        if (![[self currentVC] isKindOfClass: [LoginViewController class]]) {
                            LoginViewController *vc = [LoginViewController new];
                            vc.backBtn.hidden = YES;
                            [[self currentVC].navigationController pushViewController:vc animated:YES];
                        }
                        return;
                    }
                    NSString *str = [dict valueForKey:@"msg"];
                    return [Toast msg:str];
                }
            }
            else if ([self.action hasPrefix:@"upload"])
                self.success(@"上传成功");
            else if ([self.action hasPrefix:@"download"])
                self.success(@"下载完成");
        });
    }
    [session finishTasksAndInvalidate];
}

#pragma mark -- NSURLSessionTaskDelegate
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
    float f = 100.0 * totalBytesSent / totalBytesExpectedToSend;
    self.progress.localizedDescription = [NSString stringWithFormat:@" 已上传===%.2f%% ",f];
    if (self.loadProgress)
        dispatch_async(dispatch_get_main_queue(), ^{
            self.loadProgress(session,self.progress);
        });
}

#pragma mark -- NSURLSessionDownloadDelegate
//下载完成
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
    //写入沙盒文件
    NSString *tmpPath = NSTemporaryDirectory();
    NSString *tmpLocalPath = [tmpPath stringByAppendingPathComponent: downloadTask.response.suggestedFilename];
    NSError *error;
    [[NSFileManager defaultManager] moveItemAtPath:location.path toPath:tmpLocalPath error:&error];
    if (self.success)
        dispatch_async(dispatch_get_main_queue(), ^{
            self.success(tmpLocalPath);
        });
    [session finishTasksAndInvalidate];
}
//续传的方法
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
{
    NSLog(@"续传");
}
//获取进度（会多次调用）
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
    CGFloat f = 100.0 *totalBytesWritten / totalBytesExpectedToWrite;
    self.progress.localizedDescription = [NSString stringWithFormat:@" 已经下载===%.2f%% ",f];
    if (self.loadProgress)
        dispatch_async(dispatch_get_main_queue(), ^{
            self.loadProgress(session,self.progress);
        });
}

@end
